;;;;;;;;;;;;;;;;
; AMBIENT CHAT ;
;;;;;;;;;;;;;;;;

; TEXT-MESSAGE
(defstruct text-message from content)

; LOCAL-FACADE
(defclass im-local-facade (remote-object)
  ((username :initarg :username :accessor im-local-facade-username)
   (buddies :initarg :buddies :accessor im-local-facade-buddies)))

(defmethod talk (local-facade to text)
    (let ((buddy (gethash to (im-local-facade-buddies local-facade))))
      (if buddy
          (let ((message (make-text-message :from (im-local-facade-username local-facade) :content text)))
            (send buddy (receive buddy message) nil)
            (format *screen-output* "Message to ~D: ~D~%" to text))
        (format *screen-output* "Unknown body: ~D~%" to))))

; REMOTE-FACADE
(defclass im-remote-facade (remote-object)
  ((username :initarg :username :accessor im-remote-facade-username)))

(defmethod receive (remote-facade message)
    (format *screen-output* "Message from ~D: ~D~%" (text-message-from message) (text-message-content message)))

(defmethod get-username ((remote-facade im-remote-facade))
  (send remote-facade (im-remote-facade-username remote-facade)))

; INSTANT MESSENGER CREATION
(defun create-instant-messenger (username)
  (let* ((buddies (make-hash-table :test #'equal))
         (local-facade (make-instance 'im-local-facade :username username :buddies buddies))
         (remote-facade (make-instance 'im-remote-facade :username username))
         (instant-messenger "_p2pchat._tcp."))
    (make-actor instant-messenger local-facade remote-facade)
    (publish-service remote-facade instant-messenger)

    (whenever-discovered instant-messenger
                         (lambda (messenger)
                           (when-resolved (send messenger (im-remote-facade-username messenger))
                                          (lambda (name)
                                            (unless (or (gethash name buddies)
                                                        (equal name (im-local-facade-username local-facade)))
                                              (setf (gethash name buddies) messenger)
                                              (format *screen-output* "~D added to ~D's buddy list~%" name username)
                                              (when-disconnected messenger
                                                                 (lambda (messenger)
                                                                   (format *screen-output* "~D offline!~%" name)))
                                              (when-reconnected messenger
                                                                (lambda (messenger)
                                                                  (format *screen-output* "~D online!~%" name))))))))
    (start-network)
    local-facade))

#| Bob's device
(defvar *bob-im* (create-instant-messenger "Bob"))
|#

#| Bob's device
(talk *bob-im* "Alice" "Hi, Alice")
|#

#| Bob's device
(talk *bob-im* "Alice" "how are you?")
|#

; DISCONNECTION

#| Bob's device
(talk *bob-im* "Alice" "Anyway, I didn't want to talk to you either.")
|#

; RECONNECTION

#| Bob's device
(talk *bob-im* "Alice" "Ok")
|#
